﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using  PI_CS_Library.Virtual_Network_Information_Model;
using  PI_CS_Library.PicsNetwork_Information_Model;
using PI_CS_Tools;

namespace  PI_CS_Library
{
    public class RoutingController
    {
        //Dictionary<string, double> _linkWeightList;
        //Dictionary<string, double> _matrixWeightList;
        //Dictionary<string, PicsLink> _efficientLinks;

        private PicsNetwork _network;
        bool _avgReference;
        float _linkDelayRef;
        float _linkErrorRateRef;
        float _matrixDelayRef;

        public RoutingController(PicsNetwork _network)
        {
            this._network = _network;
            _avgReference = true;
        }

        public void EstimateNetworkElementsWeight(NetworkCoefficients _VNFactors, int _vnId) 
        {
            double _currWeight = 0;
            float _delaySum = 0;
            float _errorRateSum = 0;
            if (_avgReference)
            {
                foreach (PicsLink _link in _network.LinkResourceList.Values)
                {
                    _delaySum += _link.Parameters.Delay;
                    _errorRateSum += _link.Parameters.ErrorRate;
                }
                _linkDelayRef = _delaySum / _network.LinkResourceList.Count;
                _linkErrorRateRef = _errorRateSum / _network.LinkResourceList.Count;

                _delaySum = 0;

                foreach (PicsMatrix _matrix in _network.MatrixResourceList.Values)
                {
                    _delaySum += _matrix.Parameters.Delay;
                }

                _matrixDelayRef = _delaySum / _network.MatrixResourceList.Count;
            }


            foreach (PicsLink _link in _network.LinkResourceList.Values)
            {
                    _currWeight = _VNFactors.Delay * (_link.Parameters.Delay / _linkDelayRef) +
                    _VNFactors.ErrorRate * (_link.Parameters.ErrorRate/_linkErrorRateRef) +
                    _VNFactors.Usage * (1-_link.Parameters.AvailableCapacity/_link.Parameters.Capacity);

                _link.VnWeightList[_vnId] =_currWeight;
            }
            foreach (PicsMatrix _matrix in _network.MatrixResourceList.Values)
            {
                _currWeight = _VNFactors.Delay * (_matrix.Parameters.Delay/_matrixDelayRef);

                _matrix.VnWeightList[_vnId] =_currWeight;
            }

            /*
            foreach (string _vlink in _requestedlinksList)
            {
                foreach (Link _link in _network.Links)
                {
                    if (_link.Parameters.Delay <= _vlink.Requirements.Delay &&
                        _link.Parameters.ErrorRate <= _vlink.Requirements.ErrorRate &&
                        _link.BandwidthAvailable >= _vlink.Requirements.Bandwidth)
                        _validLinks.Add(_link);
                }

            }
             */
        }

        public void RemoveNetworkWeights(int _vnId)
        {
            foreach (PicsLink _link in _network.LinkResourceList.Values)
                _link.VnWeightList.Remove(_vnId);
            foreach (PicsMatrix _matrix in _network.MatrixResourceList.Values)
                _matrix.VnWeightList.Remove(_vnId);
  
        }

        public PicsVirtualLink FindOptimalVirtualLink(Identification _initialNodeId, Identification _dstNodeId,
            LinkParameters _linkRequirements, Identification _vnoId, int _vnId)
        {
            Dictionary<int, PicsVirtualLink> _minWeightVirtualLinks = new Dictionary<int, PicsVirtualLink>();

            //List<SwitchingMatrix> _handledNodes = new List<SwitchingMatrix>();

            foreach (int _nodeId in _network.MatrixResourceList.Keys)
            {
                _minWeightVirtualLinks.Add(_nodeId, new PicsVirtualLink(_vnId, int.MaxValue, _linkRequirements));
            }

            Identification _currentNodeId = _initialNodeId;

            PicsVirtualLink _currentVirtualLink = new PicsVirtualLink(_vnId, _linkRequirements);

            _minWeightVirtualLinks = ProcessNeighbors(_currentNodeId, _currentVirtualLink, _minWeightVirtualLinks, 
                _dstNodeId, _vnId);

            if (_minWeightVirtualLinks[_dstNodeId.Number].PicsLinkList.Count == 0)
                throw new ResourceException("Insufficient resources for a virtual link");

            return _minWeightVirtualLinks[_dstNodeId.Number];
            /*
            _shortestRoutes[_initialNode].Add() WeightSum = _initialNode.Weight; 

            while (_handledNodes.Count != _nodes.Count)
            {
                List<SwitchingMatrix> _closestNodes = (List<SwitchingMatrix>)(from s in _shortestRoutes orderby s.Value.WeightSum select s.Key).ToList();

                SwitchingMatrix _currentNode = null;
                
                foreach (SwitchingMatrix _node in _closestNodes)
                {
                    if (!_handledNodes.Contains(_node))
                    {
                        if (_shortestRoutes[_node].WeightSum == int.MaxValue)
                            return _shortestRoutes;
                        _currentNode = _node;
                        break;
                    }
                }
                

                foreach (Link _link in _currentLinks)
                {
                    if (_shortestRoutes[_link.EndB].WeightSum > _link.Weight + _link.EndB.Weight + _shortestRoutes[_link.EndA].WeightSum)
                    {
                        _shortestRoutes[_link.EndB].Links = _shortestRoutes[_link.EndA].Links.ToList();
                        _shortestRoutes[_link.EndB].Links.Add(_link);
                        _shortestRoutes[_link.EndB].WeightSum = _link.Weight + _link.EndB.Weight + _shortestRoutes[_link.EndA].WeightSum;
                    }
                }

                _handledNodes.Add(_currentNode);
                
 
            }
                 */

        }

        private Dictionary<int, PicsVirtualLink> ProcessNeighbors(Identification _currentNodeId, PicsVirtualLink _currentVirtualLink,
            Dictionary<int, PicsVirtualLink> _minWeightVirtualLinks, Identification _dstNodeId, int _vnId)
        {
            Identification _vnoId = _network.GetVirtualNetwork(_currentVirtualLink.vnId).VnoId;

            var _currentLinks = from c in _network.LinkResourceList.Values where c.SrcMatrix.Id.Number == _currentNodeId.Number select c;

            List<PicsLink> _currentLinksList = _currentLinks.ToList();

            List<PicsLink> _currentLinksSorted = _currentLinksList.OrderBy(x => (int)(x.VnWeightList[_vnId] 
                + _network.MatrixResourceList[x.SinkMatrix.Id.Number].VnWeightList[_vnId])).ToList();

            // check every link that's originating in the last node (in weight order)
            foreach (PicsLink _currentLink in _currentLinksSorted)
            {
                bool _visitedNode = false;

                // check if this single link doesn't exceed the limit - e.g. it has enough capacity
                if (_currentLink.Parameters.AvailableCapacity < _currentVirtualLink.Requirements.Capacity || _currentLink.Parameters.Delay > _currentVirtualLink.Requirements.Delay
                    || _currentLink.Parameters.ErrorRate > _currentVirtualLink.Requirements.ErrorRate) continue;

                // check if this node wasn't already visited
                foreach (PicsLink _virtualLinkPart in _currentVirtualLink.PicsLinkList.Values)
                {
                    if (_virtualLinkPart.SrcMatrix.Id == _currentLink.SinkMatrix.Id || _virtualLinkPart.SinkMatrix.Id == _currentLink.SinkMatrix.Id)
                    {
                        _visitedNode = true;
                        break;
                    }
                }

                // introducing variables for new values of virtual link's delay and error rate

                float _newLinkErrorRate;
                float _newLinkDelay;

                _newLinkErrorRate = 1-((1-_currentLink.Parameters.ErrorRate)*(1-_currentVirtualLink.Parameters.ErrorRate));

                _newLinkDelay = _currentVirtualLink.Parameters.Delay + _currentLink.Parameters.Delay + _network.MatrixResourceList[_currentLink.SinkMatrix.Id.Number].Parameters.Delay;

                // check if this virtual link wouldn't already have larger weight than the best route to destination node (also check the delay and error rate)
                if (!_visitedNode && _minWeightVirtualLinks[_dstNodeId.Number].WeightSum > _currentVirtualLink.WeightSum + _currentLink.VnWeightList[_vnId]
                    + _network.MatrixResourceList[_currentLink.SinkMatrix.Id.Number].VnWeightList[_vnId] && _newLinkDelay <= _currentVirtualLink.Requirements.Delay
                    && _newLinkErrorRate <= _currentVirtualLink.Requirements.ErrorRate)
                {
                    // check if it's the best route to a given node and then update the _minWeightVirtualLinks list and procced to this node
                    if (_currentVirtualLink.WeightSum + _currentLink.VnWeightList[_vnId] + _network.MatrixResourceList[_currentLink.SinkMatrix.Id.Number].VnWeightList[_vnId] 
                        < _minWeightVirtualLinks[_currentLink.SinkMatrix.Id.Number].WeightSum)
                    {

                        PicsVirtualLink _nextVirtualLink = new PicsVirtualLink(_currentVirtualLink);

                        _nextVirtualLink.AddPicsLink(_currentLink, _currentLink.SinkMatrix);

                        _minWeightVirtualLinks[_currentLink.SinkMatrix.Id.Number] = _nextVirtualLink;

                        if (_currentLink.SinkMatrix.Id != _dstNodeId)
                            ProcessNeighbors(_currentLink.SinkMatrix.Id, _nextVirtualLink, _minWeightVirtualLinks, _dstNodeId, _vnId);
                    }

                    // however route to this node isn't optimal (there's one with smaller weight), there's no route to the dst yet, and in case
                    //current best route exceeds the maximum delay, every other route with smaller delay have to be considered
                    else if ((_newLinkDelay < _minWeightVirtualLinks[_currentLink.SinkMatrix.Id.Number].Parameters.Delay 
                        || _newLinkErrorRate < _minWeightVirtualLinks[_currentLink.SinkMatrix.Id.Number].Parameters.ErrorRate)
                            && _minWeightVirtualLinks[_dstNodeId.Number].WeightSum == int.MaxValue)
                    {
                        PicsVirtualLink _nextVirtualLink = new PicsVirtualLink(_currentVirtualLink);

                        _nextVirtualLink.AddPicsLink(_currentLink, _currentLink.SinkMatrix);

                        if (_currentLink.SinkMatrix.Id != _dstNodeId)
                            ProcessNeighbors(_currentLink.SinkMatrix.Id, _nextVirtualLink, _minWeightVirtualLinks, _dstNodeId, _vnId);
                    }
                }
            }

            return _minWeightVirtualLinks;
        }

        public bool AvgReference
        {
            get { return _avgReference; }
            set { _avgReference = value; }
        }

        public float LinkDelayRef
        {
            get { return _linkDelayRef; }
            set { _linkDelayRef = value; }
        }

        public float LinkErrorRateRef
        {
            get { return _linkErrorRateRef; }
            set { _linkErrorRateRef = value; }
        }

        public float MatrixDelayRef
        {
            get { return _matrixDelayRef; }
            set { _matrixDelayRef = value; }
        }
    }


    class ResourceException : Exception
    {
        public ResourceException(string errorMessage)
                   : base(errorMessage) {}
    }

}
